﻿using DotNEToolkit;
using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Diagnostics;
using System.Drawing;
using System.Linq;
using System.Runtime.InteropServices;
using System.Text;
using System.Threading;
using System.Threading.Tasks;
using System.Windows.Forms;

namespace WinformToolkit
{
    /// <summary>
    /// 可以把一个其他的进程嵌入到界面里
    /// </summary>
    public partial class ApplicationContainer : UserControl
    {
        private static log4net.ILog logger = log4net.LogManager.GetLogger("ApplicationContainer");

        #region 实例变量

        private Process process;

        #endregion

        #region 属性

        /// <summary>
        /// 当前嵌入的进程
        /// </summary>
        public Process Process { get { return this.process; } }

        #endregion

        #region 构造方法

        public ApplicationContainer()
        {
            InitializeComponent();
        }

        #endregion

        #region 实例方法

        private void KillProcess()
        {
            if (this.process != null)
            {
                if (!this.process.HasExited)
                {
                    this.process.Kill();
                }

                this.process = null;
            }
        }

        private bool WaitForMainWindowHandleCreated(int timeout)
        {
            int elapsed = 0;
            while (this.process.MainWindowHandle == IntPtr.Zero)
            {
                Thread.Sleep(10);
                elapsed += 10;
                if (elapsed >= 5000)
                {
                    logger.ErrorFormat("等待进程主窗口句柄创建超时");
                    return false;
                }
            }

            return true;
        }

        #endregion

        #region 公开接口

        /// <summary>
        /// 
        /// </summary>
        /// <param name="procPath"></param>
        /// <param name="timeout">当进程主窗口的句柄在5秒还是空的时候，就失败</param>
        /// <param name="args"></param>
        public void EmbedProcess(string procPath, int timeout = 5000, string args = null)
        {
            ProcessStartInfo info = new ProcessStartInfo(procPath, args);
            info.CreateNoWindow = true;
            info.WindowStyle = ProcessWindowStyle.Minimized;

            try
            {
                this.process = System.Diagnostics.Process.Start(info);

                // Wait for process to be created and enter idle condition
                this.process.WaitForInputIdle();
                this.process.Exited += this.Process_Exited;
                this.process.EnableRaisingEvents = true;

                // 这里需要等待进程主窗口句柄创建成功
                if (!this.WaitForMainWindowHandleCreated(timeout))
                {
                    this.KillProcess();
                    return;
                }

                // Put it into this form
                Win32API.SetParent(this.process.MainWindowHandle, this.Handle);

                // Remove border and whatnot
                Win32API.SetWindowLong(new HandleRef(this, this.process.MainWindowHandle), Win32API.GWL_STYLE, Win32API.WS_VISIBLE);

                // Move the window to overlay it on this window
                Win32API.MoveWindow(this.process.MainWindowHandle, 0, 0, this.Width, this.Height, true);
            }
            catch (Exception ex)
            {
                logger.Error("EmbedProcess异常", ex);
                this.KillProcess();
            }
        }

        public void EmbedWindow(IntPtr hwnd)
        {
            // Put it into this form
            Win32API.SetParent(hwnd, this.Handle);

            // Remove border and whatnot
            Win32API.SetWindowLong(new HandleRef(this, hwnd), Win32API.GWL_STYLE, Win32API.WS_VISIBLE);

            // Move the window to overlay it on this window
            Win32API.MoveWindow(hwnd, 0, 0, this.Width, this.Height, true);
        }

        #endregion

        #region 事件处理器

        private void Process_Exited(object sender, EventArgs e)
        {
            this.process = null;
        }

        #endregion

        #region 重写方法

        protected override void OnHandleDestroyed(EventArgs e)
        {
            this.KillProcess();

            base.OnHandleDestroyed(e);
        }

        protected override void OnSizeChanged(EventArgs e)
        {
            this.Invalidate();
            base.OnSizeChanged(e);
        }

        protected override void OnResize(EventArgs e)
        {
            if (this.process != null)
            {
                Win32API.MoveWindow(this.process.MainWindowHandle, 0, 0, this.Width, this.Height, true);
            }

            base.OnResize(e);
        }

        #endregion
    }
}
